/*
 *  Exercise.cpp
 *  Workout
 *
 *  Created by Matt Krugman on 4/24/10.
 *
 */


#include "Exercise.h"

////////////////////////////////////////////////////////////////////////////////

float
percentSawtooth(const int week, 
								const int totalWeeks, 
								int *pBase, int cycles, 
								const void* ref)
{
  int delta = *((int *) ref);
  float percent;
  float slope = ((float)delta) / 100.0f;
  
  // check and adjust the number of cycles to fit within our desired 
  // timeframe
  while ((totalWeeks % cycles)) {
    cycles++;
  }
  
  float a = (float)(cycles);
  
  // Every end of cycle week, drop back down
  if (!(week % cycles) && week > 0) {
    *pBase += (int)ceil((float)delta / ((float)totalWeeks));
  }
  
  // calc the percentage
  percent = slope * ((week/a) - (float)floor(week/a + 1/2)) + (float)*pBase / 100.0f;
  
  // check for upper ceiling
  percent = percent > 1.0f ? 1.0f : percent;
  
  return percent;
}

////////////////////////////////////////////////////////////////////////////////

float
percentFlat(const int week, 
						const int totalWeeks, 
						int *pBase, int cycles, 
						const void* ref)
{
  float percent;
  
  // check and adjust the number of cycles to fit within our desired 
  // timeframe
  while ((totalWeeks % cycles)) {
    cycles++;
  }
  
  //float a = (float)(cycles) * week;
  
  // calc the percentage
  percent = (float)*pBase / 100.0f;
  
  // check for upper ceiling
  percent = percent > 1.0f ? 1.0f : percent;
  
  return percent;
}

////////////////////////////////////////////////////////////////////////////////

bool filterOlympic(const ExerciseType e)
{
  
  switch (e) {
    case Snatch:
    case CleanAndJerk:
    case JerkRack:
    case JerkPause:
      return true;
      break;
    default:
      break;
  }
  
  // Otherwise no match
  return false;
  
}

////////////////////////////////////////////////////////////////////////////////

bool filterCore(const ExerciseType e)
{
  
  switch (e) {
    case Crunches:
      return true;
      break;			
    default:
      break;
  }
  
  // Otherwise no match
  return false;
  
}

////////////////////////////////////////////////////////////////////////////////

bool filterBodyWeight(const ExerciseType e)
{
  
  switch (e) {
    case Pullups:
		case Pushups:
      return true;
      break;			
    default:
      break;
  }
  
  // Otherwise no match
  return false;
  
}

////////////////////////////////////////////////////////////////////////////////

bool filterClimbing(const ExerciseType e)
{
  
  switch (e) {
    case Climbing:
      return true;
      break;
    default:
      break;
  }
  
  // Otherwise no match
  return false;
  
}

////////////////////////////////////////////////////////////////////////////////

bool filterPress(const ExerciseType e)
{
  
  switch (e) {
    case PBN:
    case PPBN:
    case PPress:
      return true;
      break;
    default:
      break;
  }
  
  // Otherwise no match
  return false;
  
}

////////////////////////////////////////////////////////////////////////////////

bool filterRunning(const ExerciseType e)
{
  
  switch (e) {
    case Running:
      return true;
      break;
    default:
      break;
  }
  
  // Otherwise no match
  return false;
  
}

////////////////////////////////////////////////////////////////////////////////


int WeightRound(int w, int round)
{
  
  
  int weight = w;
  
  // Round the result to nearest 5 pound increments
  if (round > 0) {
    
    int weightmodulus = weight % round;
    
#if 0
    printf("weightmodulus >= %d\n",(round >> 1) + 1);
#endif
    
    if (weightmodulus >= ((round >> 1) + 1))
      weight += round - weightmodulus;
    else
      weight -= weightmodulus;
    
  }
  
  return weight;
  
}

////////////////////////////////////////////////////////////////////////////////


int calcFlat(const int steps, 
						 const int totalSteps, 
						 const int finishWeight,
						 const float goalPercent,
						 const float slopeBasePercent,
						 float *pPercent)
{
  
  float result = 0 * steps * totalSteps;
  
  // calculate the delta percentage based on the number of total steps
  result = goalPercent * (float)finishWeight;
  
  // Calculate the change in percent over the spectrum
  *pPercent = 1.0f;
  
  return (int)finishWeight;
  
}

////////////////////////////////////////////////////////////////////////////////


int calcLinear(const int steps, 
							 const int totalSteps, 
							 const int finishWeight,
							 const float goalPercent,
							 const float slopeBasePercent,
							 float *pPercent)
{
  
  float result = -1;
  float deltaPercent = (1.0f - slopeBasePercent) / (float) (totalSteps-1);
  
  // Calculate the change in percent over the spectrum
  *pPercent = deltaPercent*steps + slopeBasePercent;
  
  // Add it all up
  result = ((float) finishWeight) * *pPercent;
  
  if (slopeBasePercent >= 1.0f) {
    result = finishWeight;
  }
  
#if 1
  
  if (steps == 0)
    printf("---------------------------------------------------\n");
  
  printf("steps=%d total=%d"
				 "\tcalcLinear: basePercent=%2.2f goalPercent=%2.2f "
				 "\tpPercent=%2.2f \tdeltaPercent=%2.2f "
				 "\tfinishWeight=%d \tresult=%d"
				 "\n",
				 steps,
				 totalSteps,
				 slopeBasePercent,
				 goalPercent,
				 *pPercent,
				 deltaPercent,
				 finishWeight,
				 (int)result);
#endif
  
  return (int)result;
  
}

////////////////////////////////////////////////////////////////////////////////


int calcSine(const int steps, 
						 const int totalSteps, 
						 const int finishWeight,
						 const float goalPercent,
						 const float slopeBasePercent,
						 float *pPercent)
{
  
  float result = goalPercent;
  float deltaPercent = (1.0f - slopeBasePercent) / (float) (totalSteps-1);
  float x = 0.0f;
  
  // calculate the delta percentage based on the number of total steps	
  *pPercent = deltaPercent*steps + slopeBasePercent;
  
#if 0
  
  if (steps == 0)
    printf("---------------------------------------------------\n");
  
  printf("steps=%d total=%d"
				 "\tcalcSine: goalPercent=%2.2f "
				 "\tpPercent=%2.2f \tdeltaPercent=%2.2f\n",
				 steps,
				 totalSteps,
				 goalPercent,
				 *pPercent,
				 deltaPercent);
#endif
  
  // x = pi/2 * x^(pi/2)
  x = PI/2.0f * (float)pow(*pPercent, PI/2.0f);
  
  // Calculate the change in percent over the spectrum
  result = (float)sin(x) * (float)finishWeight;
  
  return (int)result;
  
}

////////////////////////////////////////////////////////////////////////////////


int calcTriangle(const int steps, 
								 const int totalSteps, 
								 const int finishWeight,
								 const float goalPercent,
								 const float slopeBasePercent,
								 float *pPercent)
{
  
  float result = goalPercent;
  float deltaPercent = (1.0f - slopeBasePercent) / (float) (totalSteps-1);
  
  // calculate the delta percentage based on the number of total steps	
  *pPercent = deltaPercent*steps + slopeBasePercent;
  
#if 1
  
  if (steps == 0)
    printf("---------------------------------------------------\n");
  
  printf("steps=%d total=%d"
				 "\tcalcTriangle: slopeBasePercent=%2.2f goalPercent=%2.2f "
				 "\tpPercent=%2.2f \tdeltaPercent=%2.2f\n",
				 steps,
				 totalSteps,
				 slopeBasePercent,
				 goalPercent,
				 *pPercent,
				 deltaPercent);
#endif
  
  // Calculate the change in percent over the spectrum
  result = (float)finishWeight;
  
  return (int)result;
  
}

////////////////////////////////////////////////////////////////////////////////


int calcUnitStep(const int steps, 
								 const int totalSteps, 
								 const int finishWeight,
								 const float goalPercent,
								 const float slopeBasePercent,
								 float *pPercent)
{
  
  float result = goalPercent;
	
	// a larger value for k corresponds to a sharper transition at x = 0
  float k = slopeBasePercent;

  // calculate the delta percentage based on the number of total steps	
  *pPercent = 1.0f / (float)(1 + exp(-2 * k * (steps+1)));
	
	// Make the intial set percent our base value
//	if (*pPercent < slopeBasePercent)
//		*pPercent = slopeBasePercent;
  
#if 1
  
  if (steps == 0)
    printf("---------------------------------------------------\n");
  
  printf("steps=%d total=%d"
				 "\tcalcUnitStep: slopeBasePercent=%2.2f goalPercent=%2.2f "
				 "\tpPercent=%2.2f\n",
				 steps,
				 totalSteps,
				 slopeBasePercent,
				 goalPercent,
				 *pPercent);
#endif
  
  // Calculate the change in percent over the spectrum
  result = (float)finishWeight * *pPercent;
  
  return (int)result;
  
}

////////////////////////////////////////////////////////////////////////////////
Exercise::Exercise(void) :
m_type((ExerciseType)0),
m_weightmax(-1),
m_time(0),
m_optional(false),
m_nSets(0),
m_nSetsConsolidated(0),
m_fRatio(0.0f)
{
  
}

////////////////////////////////////////////////////////////////////////////////

void 
Exercise::AddSetTypeParams(const int sets,
													 const int *reps,
													 const int goalWeight,
													 const int time,
													 const float basePercent,
													 weightCalcProc calcProc,
													 float fSlopeBasePercent /* 0.6f */)
{
  
  float percent = 0.0f;
  int weight = -1;
  
  // Build up the sets
  for (int i = 0; i < sets; i++) {
    
    // Define the set information
    // Boundary check on the reps
    m_sets[m_nSets][EXERCISE_SETS_REPS] = *reps;
    reps += 1;
    
    // Set the highest possible weight for our exercise
    // Store this for the exercise
    m_weightmax = goalWeight;
    
    // Calculate the weight based on our calculation approach
    weight = calcProc(i, sets, goalWeight, basePercent, fSlopeBasePercent, &percent);
    
    // These do not need boundary checkingh
		if (!filterBodyWeight(m_type))
			m_sets[m_nSets][EXERCISE_SETS_WEIGHT] = WeightRound(weight, 5);    
    m_sets[m_nSets][EXERCISE_SETS_TIME] = time;
    
    // Increment the number of sets for the exercise
    m_nSets++;
    
  } // end of for loop
  
  // Consolidate all the sets that are the same
  int wTempCurrent;
  int wTempLast;
  
  int rTempCurrent;
  int rTempLast;
  
  // Set consolidation
  
  m_nSetsConsolidated = -1;
  
  
  // Build up the sets so we don't have workouts that are uber long
  for (int j = 0; j < GetNumberOfSets(); j++) {
    
    wTempCurrent = m_sets[j][EXERCISE_SETS_WEIGHT];
    rTempCurrent = m_sets[j][EXERCISE_SETS_REPS];
    
    if ((wTempCurrent == wTempLast) && (rTempCurrent == rTempLast)) {
      
      m_setsConsolidated[m_nSetsConsolidated][EXERCISE_SETS_ROUNDS]++;
      
    }
    else {
      m_nSetsConsolidated++;
      m_setsConsolidated[m_nSetsConsolidated][EXERCISE_SETS_WEIGHT] = wTempCurrent;
      m_setsConsolidated[m_nSetsConsolidated][EXERCISE_SETS_REPS] = rTempCurrent;
      m_setsConsolidated[m_nSetsConsolidated][EXERCISE_SETS_ROUNDS] = 1;
      
    }
    
    // Grab and store the last reading
    wTempLast = wTempCurrent;
    rTempLast = rTempCurrent;
    
    
  } // end for loop for consolidation
  
}

////////////////////////////////////////////////////////////////////////////////
void
Exercise::AddSetType(ExerciseType e,
										 setType s, 
										 const int goalWeight, 
										 const int time,
										 const float basePercent,
										 weightCalcProc calcProc /* = calcFlat */,
										 exerciseFilterProc exFilter /* = filterNothing */,
										 float fSlopeBasePercent /* = 0.6f */)
{
  
  int sets = 0;
  int reps[EXERCISE_SETS_MAX];
  int j = 0;
  
  // Initialized the reps to all zeros
  for (int i = 0; i < EXERCISE_SETS_MAX; i++)
    reps[i] = 0;
  
  SetType(e);
  
  switch (s) {
    case set1x1:
      sets = 1;
      reps[0] = 1;
      break;
		case set1x5:
      sets = 1;
      reps[0] = 5;
      break;
		case set1x10:
      sets = 1;
      reps[0] = 10;
      break;
		case set1x20:
      sets = 1;
      reps[0] = 20;
      break;	
		case set2x5:
      sets = 2;
      reps[0] = 5;
			reps[1] = 5;
      break;		
    case set3x1:
      if (exFilter(m_type) && exFilter == filterOlympic) {
				sets = 5;
				reps[0] = 3;
				reps[1] = 2;
				reps[2] = 1;
				reps[3] = 1;
				reps[4] = 1;
      } else {
				sets = 3;
				for (j = 0; j < sets; j++)
					reps[j] = 1;
      }					
      break;
    case set3x3:
      if (exFilter(m_type) && exFilter == filterOlympic) {
				sets = 7;
				reps[0] = 4;
				reps[1] = 3;
				reps[2] = 2;
				reps[3] = 1;
				reps[4] = 1;
				reps[5] = 1;
				reps[6] = 1;
				
      } else {
				sets = 3;
				for (j = 0; j < sets; j++)
					reps[j] = 3;
      }			
      break; 
		case set3x8:
      sets = 3;
      for (j = 0; j < sets; j++)
				reps[j] = 8;
			break;
		case set3x866:
      sets = 3;
			reps[0] = 8;
			reps[1] = 6;
			reps[2] = 6;
			break;	
		case set3x10:
      sets = 3;
      for (j = 0; j < sets; j++)
				reps[j] = 10;   
			break;
    case set4x1:
      sets = 4;
      for (j = 0; j < sets; j++)
				reps[j] = 1;      
      break;  			
    case set4x2:
      sets = 4;
      for (j = 0; j < sets; j++)
				reps[j] = 2;      
      break;
    case set4x3:
      sets = 4;
      for (j = 0; j < sets; j++)
				reps[j] = 3;      
      break; 			
    case set4x5:
      sets = 4;
      for (j = 0; j < sets; j++)
				reps[j] = 5;      
      break; 			
    case set5x1:
      sets = 5;
      for (j = 0; j < sets; j++)
				reps[j] = 1;      
      break;  
    case set5x3:
      sets = 5;
      for (j = 0; j < sets; j++)
				reps[j] = 3;      
      break;
    case set5x5:
      if (exFilter(m_type) && exFilter == filterOlympic) {
				sets = 9;
				reps[0] = 4;
				reps[1] = 3;
				reps[2] = 2;
				reps[3] = 1;
				reps[4] = 1;
				reps[5] = 1;
				reps[6] = 1;
				reps[7] = 1;
				reps[8] = 1;
      } else {
				sets = 5;
				for (j = 0; j < sets; j++)
					reps[j] = 5;
      }
      break;
    case set5x6:
      sets = 5;
      for (j = 0; j < sets; j++)
				reps[j] = 6;
      break;
    case set5x7:
      sets = 5;
      for (j = 0; j < sets; j++)
				reps[j] = 7;
      break;			
    case set5x8:
      sets = 5;
      for (j = 0; j < sets; j++)
				reps[j] = 8;
      break;
    case set6x1:
      sets = 6;
      for (j = 0; j < sets; j++)
				reps[j] = 1;
      break;
		case set6x5:
      sets = 6;
      for (j = 0; j < sets; j++)
				reps[j] = 5;
      break; 			
    case set7x1:
      sets = 7;
      for (j = 0; j < sets; j++)
				reps[j] = 1;
      break; 				
    case set7x4:
      sets = 7;
      for (j = 0; j < sets; j++)
				reps[j] = 4;
      break; 				
    case set6x4:
      sets = 6;
      for (j = 0; j < sets; j++)
				reps[j] = 4;
      break;  
    case setTaper4x2211:
      sets = 4;
      reps[0] = 2;
      reps[1] = 2;
      reps[2] = 1;
      reps[3] = 1;
      break; 			
    case setTaper4x4333:
      sets = 4;
      reps[0] = 4;
      reps[1] = 3;
      reps[2] = 3;
      reps[3] = 3;
      break;   			
    case setTaper4x5533:
      sets = 4;
      reps[0] = 5;
      reps[1] = 4;
      reps[2] = 3;
      reps[3] = 3;
      break;        
    case setTaper4x7555:
      sets = 4;
      reps[0] = 7;
      reps[1] = 5;
      reps[2] = 5;
      reps[3] = 5;
      break;    		
    case setTaper5x54433:
      sets = 5;
      reps[0] = 5;
      reps[1] = 4;
      reps[2] = 4;
      reps[3] = 3;
      reps[4] = 3;
      break;    	
    case setTaper5x66655:
      sets = 5;
      reps[0] = 6;
      reps[1] = 6;
      reps[2] = 6;
      reps[3] = 5;
      reps[4] = 5;
      break;   				
    case setTaper5x77555:
      sets = 5;
      reps[0] = 7;
      reps[1] = 7;
      reps[2] = 5;
      reps[3] = 5;
      reps[4] = 5;
      break;      
    case setTaper5x88666:
      sets = 5;
      reps[0] = 8;
      reps[1] = 8;
      reps[2] = 6;
      reps[3] = 6;
      reps[4] = 6;
      break;
    case setTaper6x211111:
      sets = 6;
      reps[0] = 2;
      reps[1] = 1;
      reps[2] = 1;
      reps[3] = 1;
      reps[4] = 1;
      reps[5] = 1;
      break; 		
    case setTaper6x221111:
      sets = 6;
      reps[0] = 2;
      reps[1] = 2;
      reps[2] = 1;
      reps[3] = 1;
      reps[4] = 1;
      reps[5] = 1;
      break; 	
    case setTaper6x332222:
      sets = 6;
      reps[0] = 3;
      reps[1] = 3;
      reps[2] = 2;
      reps[3] = 2;
      reps[4] = 2;
      reps[5] = 2;
      break; 				
    case setTaper6x3_1:
      sets = 6;
      reps[0] = 3;
      reps[1] = 2;
      reps[2] = 1;
      reps[3] = 1;
      reps[4] = 1;
      reps[5] = 1;
      break; 
    case setTaper7x6_4:
      sets = 7;
      reps[0] = 6;
      reps[1] = 6;
      reps[2] = 6;
      reps[3] = 4;
      reps[4] = 4;
      reps[5] = 4;
      reps[6] = 4;
      break;       
    default:
#if DEBUG
      printf("Exercise::AddSetType(%d, ...) not defined\n",s);
#endif
      break;
  } // end switch for set types
  
  
  // Add the set
  AddSetTypeParams(sets, 
									 reps, 
									 goalWeight, 
									 time, 
									 basePercent, 
									 calcProc,
									 fSlopeBasePercent);
  
  
}

////////////////////////////////////////////////////////////////////////////////

void
Exercise::SetRatio(float p)
{
  
  // boundary check on setting the ratio
  if (p < 0.0f)
    p = 0.0f;
  
  m_fRatio = p;
  
}

////////////////////////////////////////////////////////////////////////////////

const char *
Exercise::GetDescription(ExerciseType e /* = EndExerciseType */)
{
  
  if (e == EndExerciseType)
    e = m_type;
  
  switch (e) {
    case CleanAndJerk:
      return sCLEANANDJERK;
    case Snatch:
      return sSNATCH;
    case Press:
      return sPRESS;
    case Deadlift:
      return sDEADLIFT;
    case FrontSquat:
      return sFRONTSQUAT;
    case Clean:
      return sCLEAN;
    case PowerClean:
      return sPOWERCLEAN;
    case Running:
      return sRUN;
    case CleanHighPull:
      return sCLEANHIGHPULL;  
    case CleanPull:
      return sCLEANPULL;
    case CleanThreeParts:
      return sCLEANTHREEPARTS;    
    case SnatchPull:
      return sSNATCHPULL;					
    case Climbing:
      return sCLIMBING;
    case PBN:
      return sPBN;			
    case PPBN:
      return sPPBN;
    case PPress:
      return sPUSHPRESS;	
    case PowerCleanPlusClean:
      return sPOWERCLEANPLUSCLEAN;		    
    case PowerSnatchPlusSnatch:
      return sPOWERSNATCHPLUSSNATCH;				
    case PowerSnatchPlusTwoSnatch:
      return sPOWERSNATCHPLUSTWOSNATCH;					
    case JerkDips:
      return sJERKDIPS;			
    case JerkRack:
      return sJERKRACK;
    case OverheadSquat:
      return sOVERHEADSQUATS;
    case BackSquat:
      return sSQUAT;
    case CombinationA:
      return sCOMBOA;
    case CombinationB:
      return sCOMBOB;
    case CombinationC:
      return sCOMBOC;	
    case CombinationD:
      return sCOMBOD;	
    case CombinationE:
      return sCOMBOE;	
    case CombinationF:
      return sCOMBOF;	
    case CombinationG:
      return sCOMBOG;	
    case CombinationH:
      return sCOMBOH;		    
		case CombinationI:
      return sCOMBOI;				
    case Crunches:
      return sCRUNCH;
    case SnatchDrop:
      return sSNATCHDROPS;      
    case SnatchMuscle:
      return sSNATCHMUSCLE;   
    case SnatchPower:
      return sPOWERSNATCH;
    case SnatchThreeParts:
      return sSNATCHTHREEPARTS;
    case CleanTwoParts:
      return sCLEANTWOPARTS;
    case SnatchShrugs:
      return sSNATCHSHRUGS; 			
    case JerkPause:
      return sJERKPAUSE; 				
    case Pullups:
      return sPULLUPS; 		
		case Pushups:
      return sPUSHUPS; 		
    default:
      return "No Description";
      break;
  }
  
  return NULL;
  
}


////////////////////////////////////////////////////////////////////////////////

const char *
Exercise::GetAllSets(void)
{
  char buffer[1000];
  
  // Grab the pointer to the buffer
  const char * pB;
  pB = buffer;
  
  // Build up a buffer of the weights and sets for this exercise
  // Grab the exercise name
  sprintf(buffer, "%s\n",GetDescription(m_type));
  
  // Grab the number of sets
  for (int i = 0; i <= m_nSetsConsolidated; i++) {
    
    char bucket[500];
    
    sprintf(bucket, "  %d x %d ",
						m_setsConsolidated[i][EXERCISE_SETS_REPS], 
						m_setsConsolidated[i][EXERCISE_SETS_WEIGHT]);
    
    strcat(buffer, bucket);
    
    
    if (m_setsConsolidated[i][EXERCISE_SETS_ROUNDS] > 1) {
      sprintf(bucket, 
							"(x%d)\n", 
							m_setsConsolidated[i][EXERCISE_SETS_ROUNDS]);
      
    }
    else {
      sprintf(bucket, "\n");
    }
    
    strcat(buffer, bucket);
    
  }
  
  return pB;
  
}

////////////////////////////////////////////////////////////////////////////////

ExerciseGoals::ExerciseGoals(void) :
m_nExercises(0)
{
  
#if 0
  printf("-----------ExerciseGoals------------\n");
#endif
  
  // Initialize and set up sets, reps and time
  
  for (ExerciseType e = (ExerciseType)0; 
       e < EndExerciseType; 
       e = static_cast<ExerciseType>(e+1)) 
  {
    
    // Increment the number of exercises that we have
    // Do not count the first one	
    m_nExercises++;
    
  }
  
  
  for (int i = 0; i < m_nExercises; i++) {
    
    // We know for a goal, we are only going to do this once. 
    // Therefore, set the reps to 1
    // We do NOT know the goal weight, so we set it negative
    // We do NOT know the time, so, we set it to 0    
    m_exercises[i].AddSetType((ExerciseType)i, 
															set1x1, 
															-1, 
															0, 
															EXERCISE_BASE_PERCENT, 
															calcFlat, 
															filterNothing);
    
  }
  
}

////////////////////////////////////////////////////////////////////////////////

bool 
ExerciseGoals::ExerciseGoalSet(ExerciseType e, 
															 const int weight, 
															 const int time /* = 0 */)
{
  
  int value = weight;
  
  // Boundary check for on the exercise index
  if (e > EndExerciseType) {
    return false;
  }
  
  if (e == Running)
    value = RUNNING_CONV_MILES2FT(weight);
  
  // Set the goals for the exercise
  m_exercises[e].SetWeightMax(value);
  m_exercises[e].SetTime(time);
  
  return true;
  
}

////////////////////////////////////////////////////////////////////////////////

int
ExerciseGoals::ExerciseGoalGet(ExerciseType e, 
															 int *pWeight /* = NULL */, 
															 int *pTime /* = NULL */)
{
  
  // Boundary check on the ExerciseType
  if (e == EndExerciseType)
    return false;
  
  // Make sure we have defined a set
  if (m_exercises[e].GetNumberOfSets() < 1) {
    
#if DEBUG		
    printf("WARNING: ExerciseGoal set has never been set. "
					 "m_nSets = %d\n",
					 m_exercises[e].GetNumberOfSets());
#endif
    
    return false;
  }
  
  // Otherwise we should retrieve the values
  if (pWeight)
    *pWeight = m_exercises[e].GetWeightMax();
	
	m_exercises[e].GetWeightMax();
  
  if (pTime)
    *pTime = m_exercises[e].GetTime();
  
  
  return m_exercises[e].GetWeightMax();
  
}

////////////////////////////////////////////////////////////////////////////////

int sExerciseConfig(Exercise *pEx, const float ratio, const int weight);

int sExerciseConfig(Exercise *pEx, const float ratio, const int weight)
{
  // We should set the ratio on defaults if standard
  pEx->SetRatio(ratio);
  
  
  // See if the weight is negative. This means it has never been set
  if (pEx->GetWeightMax() < 0.0f) {
    pEx->SetWeightMax((int)(pEx->GetRatio() * weight));
  }
  
  // Change the ratio based on clean and jerk if necessary
  float ratioCheck = (float)pEx->GetWeightMax()/(float)weight;
  if (ratio != ratioCheck)
    pEx->SetRatio(ratioCheck);
  
  // Return the resulting weight
  return pEx->GetWeightMax();
  
  
}

////////////////////////////////////////////////////////////////////////////////

bool
ExerciseGoals::ExerciseGoalsSetAll(void)
{
  
  // CHeck to see if clean and jerk is defined. at minimum, we can base
  // all measurements on this value
  // We sometimes know other max values that can help shape our lifts
  
  int cnj = m_exercises[CleanAndJerk].GetWeightMax();
  int sna = -1;
  int bs = -1;
  int fs = -1;
  int pr = -1;
  int dl = -1;
  
  
  if (cnj == -1) {
#if DEBUG
    printf("SetAllGoals can not properly execute."
					 "Since clean and jerk not defined. Define this first\n");
#endif
    return false;
  }
  
#if DEBUG
  printf("-----------ExerciseGoalsSetAll------------\n");
#endif
  
  
  //calculate all other goals based on our results.
  for (ExerciseType e = (ExerciseType) 0; 
       e < EndExerciseType; 
       e = static_cast<ExerciseType>(e+1)) 
  {
    
    switch (e) {
      case CleanAndJerk:
				cnj = sExerciseConfig(&m_exercises[e], P_CLEANANDJERK, cnj);
				break;
      case Snatch:
				sna = sExerciseConfig(&m_exercises[e], P_SNATCH, cnj);
				break;
      case BackSquat:
				bs = sExerciseConfig(&m_exercises[e], P_BACKSQUAT, cnj);
				break;
      case FrontSquat:
				fs = sExerciseConfig(&m_exercises[e], P_FRONTSQUAT, cnj);
				break;
      case Press:
				pr = sExerciseConfig(&m_exercises[e], P_PRESS, cnj);
				break;
      case Deadlift:
				dl = sExerciseConfig(&m_exercises[e], P_DEADLIFT, cnj);
				break;
      case Clean:
				sExerciseConfig(&m_exercises[e], P_CLEAN, cnj);
				break;
      case PowerClean:
				sExerciseConfig(&m_exercises[e], P_POWERCLEAN, cnj);
				break;
      case CleanTwoParts:
				sExerciseConfig(&m_exercises[e], P_CLEANTWOPARTS, cnj);
				break;
      case CleanThreeParts:
				sExerciseConfig(&m_exercises[e], P_CLEANTHREEPARTS, cnj);
				break;					
      case OverheadSquat:
				sExerciseConfig(&m_exercises[e], P_OVERHEADSQUATS, sna);
				break;
      case JerkRack:
				sExerciseConfig(&m_exercises[e], P_JERKRACK, cnj);
				break;
      case JerkDips:
				sExerciseConfig(&m_exercises[e], P_JERKDIPS, cnj);
				break;				
      case PBN:
				sExerciseConfig(&m_exercises[e], P_PBN, pr);
				break;				
      case PPBN:
				sExerciseConfig(&m_exercises[e], P_PPBN, pr);
				break;
      case PPress:
				sExerciseConfig(&m_exercises[e], P_PUSHPRESS, pr);
				break;				
      case Running:
				sExerciseConfig(&m_exercises[e], 1.0f, 5280);
				break;
      case CombinationA:
				sExerciseConfig(&m_exercises[e], P_COMBOA, cnj);
				break;				
      case CombinationB:
				sExerciseConfig(&m_exercises[e], P_COMBOB, cnj);
				break;			
      case CombinationC:
				sExerciseConfig(&m_exercises[e], P_COMBOC, cnj);
				break;					
      case CombinationD:
				sExerciseConfig(&m_exercises[e], P_COMBOD, cnj);
				break;			
      case CombinationE:
				sExerciseConfig(&m_exercises[e], P_COMBOE, cnj);
				break;
      case CombinationF:
				sExerciseConfig(&m_exercises[e], P_COMBOF, cnj);
				break;	
      case CombinationG:
				sExerciseConfig(&m_exercises[e], P_COMBOG, cnj);
				break;
      case CombinationH:
				sExerciseConfig(&m_exercises[e], P_COMBOH, sna);
				break;	
      case CombinationI:
				sExerciseConfig(&m_exercises[e], P_COMBOI, sna);
				break;					
      case Climbing:
				sExerciseConfig(&m_exercises[e], 1.0f, mCLIMBING_5p12a);
				break;
      case CleanHighPull:
				sExerciseConfig(&m_exercises[e], P_CLEANHIGHPULL, cnj);
				break;	
      case CleanPull:
				sExerciseConfig(&m_exercises[e], P_CP, cnj);
				break;
      case SnatchPull:
				sExerciseConfig(&m_exercises[e], P_SNATCHPULL, sna);
				break;
      case SnatchShrugs:
				sExerciseConfig(&m_exercises[e], P_SNATCHSHRUGS, sna);
				break;					
      case SnatchDrop:
				sExerciseConfig(&m_exercises[e], P_SNATCHDROPS, sna);
				break;		
      case SnatchMuscle:
				sExerciseConfig(&m_exercises[e], P_SNATCHMUSCLE, sna);
				break;
      case SnatchPower:
				sExerciseConfig(&m_exercises[e], P_POWERSNATCH, sna);
				break;		
      case PowerSnatchPlusSnatch:
				sExerciseConfig(&m_exercises[e], P_POWERSNATCHPLUSSNATCH, sna);
				break;					
      case PowerSnatchPlusTwoSnatch:
				sExerciseConfig(&m_exercises[e], P_POWERSNATCHPLUSTWOSNATCH, sna);
				break;	
      case PowerCleanPlusClean:
				sExerciseConfig(&m_exercises[e], P_POWERCLEANPLUSCLEAN, cnj);
				break;
      case SnatchThreeParts:
				sExerciseConfig(&m_exercises[e], P_SNATCHTHREEPARTS, sna);
				break;		
      case Crunches:
				sExerciseConfig(&m_exercises[e], 1.0f, -1);
				break;
      case JerkPause:
				sExerciseConfig(&m_exercises[e], P_JERKPAUSE, cnj);
				break;
      case Pullups:
				sExerciseConfig(&m_exercises[e], 1.0f, P_PULLUPS);
				break;	
      case Pushups:
				sExerciseConfig(&m_exercises[e], 1.0f, P_PUSHUPS);
				break;					
      default:
#if DEBUG
				printf("SetAllGoals (switch)for ExerciseType index %d not defined "
							 "for %s\n", 
							 e,
							 GetDescription(e));
				
				exit(0);
#endif
				break;
    }
    
  }
  
  
  return true;
  
}


////////////////////////////////////////////////////////////////////////////////

const char * 
ExerciseGoals::ExerciseGoalsShowAll(OutputType t, FILE *pFile /* = NULL */)
{
  
  // Initialize and set up sets, reps and time
  int w;
  int time = t;
  char buffer[1000];
  
  for (int m = 0; m < 1000; m++)
    buffer[m] = '\0';
  
  
  // Grab the pointer to the buffer
  const char * pB;
  pB = buffer;
  
  // Description of stuff
  char description[] = "Goals for workout";
  
  switch (t) {
    case OutputHTML:
      fprintf(pFile, 
							"<h2>%s</h2>\n"
							"<UL>\n",
							description);
      break;
    case OutputDebug:
      fprintf(pFile, 
							"%s\n",
							description);
      break;
      
    case OutputICAL:
      break;
  }
  
  /////////////////////////////////////////////////////////////
  // Do this for every single exercise
  for (ExerciseType e = (ExerciseType)0; 
       e < EndExerciseType; 
       e = static_cast<ExerciseType>(e+1)) 
  {
    
    char bucket[500];
    
    for (int m = 0; m < 500; m++)
      bucket[m] = '\0';
    
    // Increment the number of exercises that we have
    // Do not count the first one	
    ExerciseGoalGet(e, &w, &time);
    
    float ratio = ExerciseGoalGetRatio(e);
    
    // Match on the appropriate output type
    switch (t) {
				
      case OutputICAL:
      case OutputDebug:
				
				if (filterPress(e)) {
					
					
					fprintf(pFile, "%s = "
									"%d [%d kg] - "
									"%1.0f%% of Press"
									"\n",
									GetDescription(e),
									WeightRound(w, 0),
									(int)((float)WeightRound(w, 0) / 2.2f),
									ratio*100.0f);					
					
				}
				else {
					
					fprintf(pFile, "%s = "
									"%d [%d kg] - "
									"%1.0f%% of Clean and Jerk"
									"\n",
									GetDescription(e),
									WeightRound(w, 0),
									(int)((float)WeightRound(w, 0) / 2.2f),
									ratio*100.0f);
					
					break;
					
				}
				
      case OutputHTML:
				
				if (filterClimbing(e)) {
					fprintf(pFile,
									"<LI>%s = "
									"%s"
									"</LI>\n",
									GetDescription(e),
									getClimbingGrade(w));
				} 
				else if (filterRunning(e)) {
					fprintf(pFile,
									"<LI>%s = "
									"%1.2f miles"
									"</LI>\n",
									GetDescription(e),
									RUNNING_CONV_FT2MILES(w));	  
				}
				else if (filterPress(e)) {
					fprintf(pFile,
									"<LI>%s = "
									"%d [%1.0f kg] - "
									"%1.0f%% of Press"
									"</LI>\n",
									GetDescription(e),
									WeightRound(w, 0),
									LBS2KG(w),
									ratio*100.0f);
				}				
				else if (filterCore(e)) {
					fprintf(pFile,
									"<LI>%s - as many as necessary"
									"</LI>\n",
									GetDescription(e));	  
				}
				else if (filterBodyWeight(e)) {
					fprintf(pFile,
									"<LI>%s = "
									"%d"
									"</LI>\n",
									GetDescription(e),
									w);	  
				}		
				else {
					fprintf(pFile,
									"<LI>%s = "
									"%d [%1.0f kg] - "
									"%1.0f%% of Clean and Jerk"
									"</LI>\n",
									GetDescription(e),
									WeightRound(w, 0),
									LBS2KG(w),
									ratio*100.0f);
				}
				break;
				
    } // end switch for type
    
  } // end for loop for exercises
  
  /////////////////////////////////////////////////////////////
  
  switch (t) {
    case OutputHTML:
      fprintf(pFile,"</UL>\n");
      break;
      
    case OutputDebug:
      break;
      
    case OutputICAL:
      break;
  }
  
  return pB;
  
}


///////////////////////////////////////////////////////////////////////////

const char * 
getClimbingGrade(int value, 
								 int climbingOption /* = mCLIMBING_ROPE_OUTDOORS */)
{
  
  const char * pB;
  
  switch (climbingOption) {
    case mCLIMBING_ROPE_INDOORS:
    case mCLIMBING_ROPE_OUTDOORS:
      
      if (value <= 3) {
				return "5.0";
      }
      else if (value <= 6)
				return "5.1";
      else if (value <= mCLIMBING_5p2) {
				return "5.2";
      }
      else if (value <= mCLIMBING_5p3) {
				return "5.3";
      }
      else if (value <= mCLIMBING_5p4) {
				return "5.4";
      }
      else if (value <= mCLIMBING_5p5) {
				return "5.5";
      }
      else if (value <= mCLIMBING_5p6) {
				return "5.6";
      }
      else if (value <= mCLIMBING_5p7) {
				return "5.7";
      }
      else if (value <= mCLIMBING_5p8m) {
				return "5.8-";
      }
      else if (value <= mCLIMBING_5p8) {
				return "5.8";
      }
      else if (value <= mCLIMBING_5p8p) {
				return "5.8+";
      }
      else if (value <= mCLIMBING_5p9m) {
				return "5.9-";
      }
      else if (value <= mCLIMBING_5p9) {
				return "5.9";
      }
      else if (value <= mCLIMBING_5p9p) {
				return "5.9+";
      }
      else if (value <= mCLIMBING_5p10a){
				return "5.10a";
      }
      else if (value <= mCLIMBING_5p10b) {
				return "5.10b";
      }
      else if (value <= mCLIMBING_5p10c) {
				return "5.10c";
      }
      else if (value <= mCLIMBING_5p10d) {
				return "5.10d";
      }
      else if (value <= mCLIMBING_5p11a) {
				return "5.11a";
      }
      else if (value <= mCLIMBING_5p11b) {
				return "5.11b";
      }
      else if (value <= mCLIMBING_5p11c) {
				return "5.11c";
      }
      else if (value <= mCLIMBING_5p11d) {
				return "5.11d";
      }
      else if (value <= mCLIMBING_5p12a) {
				return "5.12a";
      }
      else if (value <= mCLIMBING_5p12b) {
				return "5.12b";
      }
      else if (value <= mCLIMBING_5p12c) {
				return "5.12c";
      }
      else if (value <= mCLIMBING_5p12d) {
				return "5.12d";
      }
      else if (value <= mCLIMBING_5p13a) {
				return "5.13a";
      }
      else if (value <= mCLIMBING_5p13b) {
				return "5.13b";
      }
      else if (value <= mCLIMBING_5p13c) {
				return "5.13c";
      }
      else if (value <= mCLIMBING_5p13d) {
				return "5.13d";
      }
      else if (value <= mCLIMBING_5p14a) {
				return "5.14a";
      }
      else if (value <= mCLIMBING_5p14b) {
				return "5.14b";
      }
      else if (value <= mCLIMBING_5p14c) {
				return "5.14c";
      }
      else {
				return "5.14d";
      }
      
      
      
      break;
      
#if 0      
      
    case mCLIMBING_BOULDER_SPOT:
      
      if (value <= mCLIMBING_SPOT1)
				aStringCopy(grade,"1 spot");
      else if (value <= mCLIMBING_SPOT1p)
				aStringCopy(grade,"1+ spot");
      else if (value <= mCLIMBING_SPOT2)
				aStringCopy(grade,"2 spot");
      else if (value <= mCLIMBING_SPOT2p)
				aStringCopy(grade,"2+ spot");
      else if (value <= mCLIMBING_SPOT3m)
				aStringCopy(grade,"3- spot");
      else if (value <= mCLIMBING_SPOT3)
				aStringCopy(grade,"3 spot");
      else if (value <= mCLIMBING_SPOT3p)
				aStringCopy(grade,"3+ spot");
      else if (value <= mCLIMBING_SPOT4m)
				aStringCopy(grade,"4- spot");
      else if (value <= mCLIMBING_SPOT4)
				aStringCopy(grade,"4 spot");
      else if (value <= mCLIMBING_SPOT4p)
				aStringCopy(grade,"4+ spot");
      else if (value <= mCLIMBING_SPOT5m)
				aStringCopy(grade,"5- spot");
      else if (value <= mCLIMBING_SPOT5)
				aStringCopy(grade,"5 spot");
      else 
				aStringCopy(grade,"5+ spot");
      
      
      break;
      
    case mCLIMBING_BOULDER_OUTDOORS:
      if (value <= mCLIMBING_V0)
				aStringCopy(grade,"V0");
      else if (value <= mCLIMBING_V0p)
				aStringCopy(grade,"V0+");
      else if (value <= mCLIMBING_V1)
				aStringCopy(grade,"V1");
      else if (value <= mCLIMBING_V2)
				aStringCopy(grade,"V2");
      else if (value <= mCLIMBING_V3)
				aStringCopy(grade,"V3");
      else if (value <= mCLIMBING_V4)
				aStringCopy(grade,"V4");
      else if (value <= mCLIMBING_V5)
				aStringCopy(grade,"V5");
      else if (value <= mCLIMBING_V6)
				aStringCopy(grade,"V6");
      else if (value <= mCLIMBING_V7)
				aStringCopy(grade,"V7");
      else if (value <= mCLIMBING_V8)
				aStringCopy(grade,"V8");
      else
				aStringCopy(grade,"V9");
      
      
      break;
      
#endif       
      
    default:
      break;
  }
  
  return pB;
  
}
